﻿using System;
using System.Runtime.CompilerServices;
using Internal.KuiHuaBaoDian.Services.Lockstep;
using KuiHuaBaoDian.Services.Lockstep;

namespace KuiHuaBaoDian.Services.Lockstep {

    /// <summary>
    /// 帧同步计时器
    /// </summary>
    public interface ILockstepTimer : ILockstepTimerData, ILockstepEntity {

        /// <summary>
        /// 委托
        /// </summary>
        /// <param name="data">发生事件的计时器数据</param>
        public delegate void DataDelegate(ILockstepTimerData data);

        /// <summary>
        /// 当唤醒时，通常是在刚刚加入到帧同步队列时发生
        /// </summary>
        event DataDelegate Awake;

        /// <summary>
        /// 当计时开始时，会在延迟后开始计时时发生
        /// </summary>
        event DataDelegate Start;

        /// <summary>
        /// 当移出帧同步队列时发生
        /// </summary>
        event DataDelegate Kill;

        /// <summary>
        /// 当完成一次每期时发生
        /// </summary>
        event DataDelegate Timer;

        /// <summary>
        /// 当完成所有周期时发生
        /// </summary>
        event DataDelegate Complete;

        /// <summary>
        /// 当计时发生改变时发生，不会在延迟阶段发生
        /// </summary>
        event DataDelegate Update;

        /// <summary>
        /// 当计时器有时间经过时发生，包括延迟阶段
        /// </summary>
        event DataDelegate Elapse;

        /// <summary>
        /// 当计时器开始新一轮时发生
        /// </summary>
        event DataDelegate TimerStart;

        /// <summary>
        /// 句柄
        /// </summary>
        LockstepTimerHandle Handle { get; }
    }

    /// <summary>
    /// 帧同步计时器数据
    /// </summary>
    public interface ILockstepTimerData {

        /// <summary>
        ///  设定的周期时间
        /// </summary>
        F64 Time { get; }

        /// <summary>
        /// 当前周期运行时间
        /// </summary>
        F64 Current { get; }

        /// <summary>
        /// 流逝的时间, 包括<see cref="Delay"/>
        /// </summary>
        F64 Elapsed { get; }

        /// <summary>
        /// 延迟开始计时的时间
        /// </summary>
        F64 Delay { get; }

        /// <summary>
        /// 需要运行的周期数
        /// </summary>
        uint Count { get; }

        /// <summary>
        /// 更新时的时间缩放量
        /// </summary>
        F64 Scale { get; }

        /// <summary>
        /// 已经运行的周期数
        /// </summary>
        uint Times { get; }

        /// <summary>
        /// 是否已经完成计时
        /// </summary>
        bool IsComplete { get; }

        /// <summary>
        /// 是否完成时自己销毁这个计时器
        /// </summary>
        bool IsAutoKill { get; }

        /// <summary>
        /// 是否强制加入到帧同步服务
        /// </summary>
        bool IsForceAttach { get; }

        /// <summary>
        /// 是否暂停中
        /// </summary>
        bool IsPausing { get; }

        /// <summary>
        /// 是否是销毁状态
        /// </summary>
        bool IsKilled { get; }
    }

    public interface ILockstepTimerSetter {

        void SetTime(F64 value);

        void SetCount(uint value);

        void SetDelay(F64 value);

        void SetIsAutoKill(bool value);

        void SetIsForceAttach(bool value);

        void SetScale(F64 value);
    }

    [Implement(typeof(ILockstepTimer))]
    internal sealed class KHBD_LockstepTimer : KHBD_Object, ILockstepTimer, ILockstepTimerSetter, ILockstepTimerInternal {

        public event ILockstepTimer.DataDelegate Awake;

        public event ILockstepTimer.DataDelegate Start;

        public event ILockstepTimer.DataDelegate Kill;

        public event ILockstepTimer.DataDelegate Timer;

        public event ILockstepTimer.DataDelegate Complete;

        public event ILockstepTimer.DataDelegate Update;

        public event ILockstepTimer.DataDelegate Elapse;

        public event ILockstepTimer.DataDelegate TimerStart;

        private event ILockstepTimerInternal.CancelledDelegate Cancelled;
        event ILockstepTimerInternal.CancelledDelegate ILockstepTimerInternal.Cancelled {
            add => Cancelled += value;
            remove => Cancelled -= value;
        }

        private event ILockstepTimerInternal.AutoKillDelegate AutoKill;
        event ILockstepTimerInternal.AutoKillDelegate ILockstepTimerInternal.AutoKill {
            add => AutoKill += value;
            remove => AutoKill -= value;
        }

        public LockstepTimerHandle Handle { get; private set; }

        public F64 Time { get; private set; } = F64.One;

        public F64 Current { get; private set; } = F64.Zero;

        public uint Count { get; private set; } = 0;

        public F64 Scale { get; private set; } = F64.One;

        public F64 Elapsed { get; private set; } = F64.Zero;

        public F64 Delay { get; private set; } = F64.Zero;

        public bool IsComplete { get; private set; } = false;

        public bool IsAutoKill { get; private set; } = true;

        public bool IsForceAttach { get; private set; } = false;

        public bool IsPausing { get; private set; } = false;

        public uint Times { get; private set; } = 0;

        public bool IsKilled { get; private set; } = true;

        private bool m_IsStarted = false;

        public void SetTime(F64 value) {
            Time = value >= F64.Zero ? value : F64.Zero;
        }

        public void SetCount(uint value) {
            Count = value;
        }

        public void SetIsAutoKill(bool value) {
            IsAutoKill = value;
        }

        public void SetIsForceAttach(bool value) {
            IsForceAttach = value;
        }

        public void SetScale(F64 value) {
            Scale = value;
        }

        public void SetDelay(F64 value) {
            Delay = value;
        }

        protected override void Dispose() {
            Recycle();
        }

        void ILockstepTimerInternal.SetIsPause(bool value) {
            IsPausing = value;
        }

        private bool m_IsCancelled = false;
        void ILockstepTimerInternal.Cancel() {
            m_IsCancelled = true;
            Cancelled?.Invoke();
        }

        void ILockstepTimerInternal.Removing() {
            m_IsCancelled = true;
        }

        void ILockstepTimerServiceInternal.IReusable.Reuse(LockstepTimerHandle handle) => Reuse(handle);
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void Reuse(LockstepTimerHandle handle) {
            Handle = handle;
            Scale = F64.One;
            IsComplete = false;
            Current = F64.Zero;
            IsPausing = false;
            Times = 0;
            Elapsed = F64.Zero;
            m_IsCancelled = false;
            m_IsStarted = false;
            IsKilled = false;
            IsForceAttach = false;
        }

        void ILockstepTimerServiceInternal.IReusable.Recycle() => Recycle();
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void Recycle() {
            IsKilled = true;
            Awake = null;
            Start = null;
            Kill = null;
            Timer = null;
            Complete = null;
            Cancelled = null;
            Update = null;
            Elapse = null;
            AutoKill = null;
            TimerStart = null;
            Handle = LockstepTimerHandle.None;
        }

        void ILockstepEntity.DoLogicFrame(int frameIndex) {
            if (m_IsCancelled) {
                return;
            }
            if (!IsComplete && !IsPausing) {
                if (!m_IsStarted && Delay > 0) {
                    var sacle = Scale >= F64.Zero ? Scale : F64.Zero;
                    if (sacle > 0) {
                        var delta = LockstepService.Instance.FrameLength;
                        delta *= sacle;
                        Elapsed += delta;
                        if (Elapsed >= Delay) {
                            if (Time <= 0) {
                                Elapsed = Delay;
                                ZeroTime();
                            } else {
                                Current = Elapsed - Delay;
                                Normal();
                            }
                        }
                    }
                } else {
                    if (Time <= 0) {
                        ZeroTime();
                    } else {
                        var sacle = Scale >= F64.Zero ? Scale : F64.Zero;
                        if (sacle > 0) {
                            var delta = LockstepService.Instance.FrameLength;
                            delta *= sacle;
                            Current += delta;
                            Elapsed += delta;
                            Normal();
                        }
                    }
                }
            }
        }

        private void CheckStart() {
            if (!m_IsStarted) {
                m_IsStarted = true;
                Start?.Invoke(this);
                TimerStart?.Invoke(this);
            }
        }

        private void ZeroTime() {
            CheckStart();
            if (Count > 0) {
                Times = Count;
                Timer?.Invoke(this);
                IsComplete = true;
                Complete?.Invoke(this);
            } else {
                Times++;
                Timer?.Invoke(this);
                TimerStart?.Invoke(this);
            }
        }

        private void Normal() {
            CheckStart();
            if (Current >= Time) {
                Current -= Time;
                Times++;
                Update?.Invoke(this);
                Timer?.Invoke(this);
                if (Count != 0 && Times >= Count) {
                    Elapsed = Time * (int)Count;
                    Current = Time;
                    IsComplete = true;
                    Elapse?.Invoke(this);
                    Complete?.Invoke(this);
                    if (IsAutoKill) {
                        AutoKill?.Invoke(Handle);
                    }
                } else {
                    Elapse?.Invoke(this);
                    TimerStart?.Invoke(this);
                }
            } else {
                Update?.Invoke(this);
                Elapse?.Invoke(this);
            }
        }

        void ILockstepEntity.OnAttached(int frameIndex) {
            Awake?.Invoke(this);
        }

        void ILockstepEntity.OnDetached() {
            Kill?.Invoke(this);
        }

        void ILockstepEntity.DoUpdate(int frameIndex, int syncedFrameCount, float deltaTime, float timeScale) {
        }
    }
}

namespace Internal.KuiHuaBaoDian.Services.Lockstep {

    public interface ILockstepTimerInternal : ILockstepTimer, ILockstepTimerSetter, ILockstepTimerServiceInternal.IReusable, IDisposable {

        public delegate void CancelledDelegate();
        public delegate void AutoKillDelegate(LockstepTimerHandle handle);

        event CancelledDelegate Cancelled;
        event AutoKillDelegate AutoKill;

        void SetIsPause(bool value);

        void Cancel();

        void Removing();
    }
}
